步骤 11:添加导出配置

安装 测试 教程中,我们添加了 CMake 安装项目库和头文件的功能。在 打包 安装程序 中,我们添加了打包这些信息的功能,以便将其分发给其他人。

下一步是添加必要的信息,以便其他 CMake 项目可以使用我们的项目,无论是从构建目录、本地安装还是打包后。

第一步是更新我们的 install(TARGETS) 命令,不仅指定 DESTINATION,还要指定 EXPORTEXPORT 关键字生成一个 CMake 文件,其中包含从安装树导入安装命令中列出的所有目标的代码。因此,让我们继续显式地 EXPORT MathFunctions 库,方法是更新 MathFunctions/CMakeLists.txt 中的 install 命令,使其看起来像这样

MathFunctions/CMakeLists.txt
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
  list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs}
        EXPORT MathFunctionsTargets
        DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)

现在我们已经 EXPORTMathFunctions,我们还需要显式地安装生成的 MathFunctionsTargets.cmake 文件。这可以通过在顶级 CMakeLists.txt 的底部添加以下内容来完成

CMakeLists.txt
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

此时,您应该尝试运行 CMake。如果一切都设置正确,您将看到 CMake 会生成一个类似于以下内容的错误

Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains
path:

  "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions"

which is prefixed in the source directory.

CMake 告诉您,在生成导出信息期间,它将导出与当前机器紧密相关的路径,并且在其他机器上将无效。解决方案是更新 MathFunctionstarget_include_directories(),使其了解在从构建目录和从安装/包使用时,它需要不同的 INTERFACE 位置。这意味着将 MathFunctionstarget_include_directories() 调用转换为以下内容

MathFunctions/CMakeLists.txt
target_include_directories(MathFunctions
                           INTERFACE
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                            $<INSTALL_INTERFACE:include>
                           )

更新完后,我们可以重新运行 CMake 并验证它不再发出警告。

此时,我们已经让 CMake 正确地打包了所需的 target 信息,但我们仍然需要生成一个 MathFunctionsConfig.cmake,以便 CMake 的 find_package() 命令可以找到我们的项目。因此,让我们继续在项目的顶级添加一个名为 Config.cmake.in 的新文件,其内容如下

Config.cmake.in

@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )

然后,为了正确配置和安装该文件,请在顶级 CMakeLists.txt 的底部添加以下内容

CMakeLists.txt
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)

接下来,我们执行 configure_package_config_file()。此命令将配置提供的文件,但与标准 configure_file() 方法有一些具体区别。要正确使用此函数,输入文件应包含一行文本 @PACKAGE_INIT@,以及所需的内容。该变量将被替换为一段代码,该代码将设置的值转换为相对路径。这些新的值可以通过相同的名称引用,但前面要加上 PACKAGE_ 前缀。

CMakeLists.txt
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)
# generate the config file that includes the exports
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION "lib/cmake/MathFunctions"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )

接下来是 write_basic_package_version_file()。此命令写入一个文件,该文件被 find_package() 使用,记录了所需包的版本和兼容性。在这里,我们使用 Tutorial_VERSION_* 变量,并说它与 AnyNewerVersion 兼容,这意味着此版本或任何更高版本都与请求的版本兼容。

CMakeLists.txt
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
  COMPATIBILITY AnyNewerVersion
)

最后,将这两个生成的文件都设置为可安装

CMakeLists.txt
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake
  DESTINATION lib/cmake/MathFunctions
  )

此时,我们已经为项目生成了一个可重定位的 CMake 配置,该配置可以在项目安装或打包后使用。如果我们希望我们的项目也从构建目录使用,我们只需要在顶级 CMakeLists.txt 的底部添加以下内容

CMakeLists.txt
export(EXPORT MathFunctionsTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)

通过此导出调用,我们现在生成一个 MathFunctionsTargets.cmake,允许构建目录中配置的 MathFunctionsConfig.cmake 被其他项目使用,而无需安装它。